package com.iwdnb.gkgz.common.utils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.alibaba.fastjson.JSON;

import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.iwdnb.bmnf.common.utils.BeanConvertUtils;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.model.dto.StockIncomePrediction;
import com.iwdnb.gkgz.common.model.dto.StockMinuteData;
import com.iwdnb.gkgz.common.model.dto.StockPredictionCheckInfo;
import com.iwdnb.gkgz.common.model.dto.StockPredictionData;
import com.iwdnb.gkgz.common.prediction.DefaultPredictionHelper;
import com.iwdnb.gkgz.common.prediction.PredictionHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

@Slf4j
public class StockPredictionUtils {

    /**
     * 预测股票的当日价格
     *
     * @param stockDayData
     * @param correctDays
     * @return
     */
    public static StockPredictionData predictionStock(StockDayData stockDayData, List<String> correctDays) {
        if (correctDays.contains(stockDayData)) {
            StockPredictionData stockPredictionData = new StockPredictionData();
            //TODO
            return stockPredictionData;
        }
        PredictionHelper predictionHelper = new DefaultPredictionHelper();
        StockPredictionData predictionData = predictionHelper.predictionStock(stockDayData);
        //PredictionHelper predictionHelper2 = new ThirdPredictionHelper();
        //StockPredictionData predictionData2 = predictionHelper2.predictionStock(stockDayData);
        //predictionData.setMaxPrice(predictionData.getMaxPrice()
        //    .add(predictionData2.getMaxPrice())
        //    .divide(new BigDecimal(2))
        //    .setScale(2,RoundingMode.HALF_DOWN));
        //predictionData.setMinPrice(predictionData.getMinPrice()
        //    .add(predictionData2.getMinPrice())
        //    .divide(new BigDecimal(2))
        //    .setScale(2,RoundingMode.HALF_UP));
        return predictionData;
    }

    public static StockPredictionCheckInfo checkPrediction(StockPredictionData predictionData,
        StockDayData stockDayData) {
        StockPredictionCheckInfo checkInfo = new StockPredictionCheckInfo();
        boolean matchSell = predictionData.getMaxPrice().compareTo(stockDayData.getMaxPrice()) <= 0;
        checkInfo.setMatchSell(matchSell);
        boolean eqSell = predictionData.getMaxPrice().compareTo(stockDayData.getMaxPrice()) == 0;
        checkInfo.setEqSell(eqSell);
        boolean matchBuy = predictionData.getMinPrice().compareTo(stockDayData.getMinPrice()) >= 0;
        checkInfo.setMatchBuy(matchBuy);
        boolean eqBuy = predictionData.getMinPrice().compareTo(stockDayData.getMinPrice()) == 0;
        checkInfo.setEqBuy(eqBuy);
        checkInfo.setMatchAll(matchSell && matchBuy);
        checkInfo.setEqAll(eqSell && eqBuy);
        return checkInfo;
    }

    /**
     * 计算收益
     *
     * @param stockDayDataList
     * @return
     */
    public static List<StockIncomePrediction> caculatePrice(List<StockDayData> stockDayDataList) {
        if (CollectionUtils.isEmpty(stockDayDataList)) {
            return null;
        }
        StockDayData firstData = stockDayDataList.get(0);
        List<StockMinuteData> historyMinuteData = null;
        if (firstData.getDate().startsWith(DateUtil.format(new Date(), "yyyy"))) {
            historyMinuteData = StockUtils.queryHistoryMinuteData(firstData.getCode(),
                Lists.newArrayList(30, 60));
        }
        BigDecimal total = new BigDecimal(0);
        BigDecimal buyPrice = stockDayDataList.get(0).getClosePrice();
        boolean first = true;
        int sleepCount = 0;
        List<BigDecimal> notSellBuyList = new ArrayList<>();
        StockPredictionData tomorroPrediction = null;
        List<StockIncomePrediction> stockIncomePredictions = new ArrayList<>();
        for (int i = 0; i < stockDayDataList.size(); i++) {
            StockDayData data = stockDayDataList.get(i);
            StockIncomePrediction stockIncomePrediction = BeanConvertUtils.convert(data, StockIncomePrediction.class);
            stockIncomePredictions.add(stockIncomePrediction);
            int tempIndex = i < stockDayDataList.size() - 1 ? i + 1 : i;
            tomorroPrediction = stockDayDataList.get(tempIndex).getTomorrowPrediction();
            if (sleepCount > 0) {
                log.info("{}割肉离场等待最后{}天,收盘价:{}", data.getDate(), sleepCount, data.getClosePrice());
                sleepCount--;
                continue;
            }
            if (buyPrice == null) {
                log.info("{}割肉离场等待结束,重新尾盘收盘价买入,收盘价:{}", data.getDate(), data.getClosePrice());
                buyPrice = data.getClosePrice();
                continue;
            }
            StockPredictionCheckInfo checkInfo = data.getCheckInfo();
            StockPredictionData prediction = data.getTodayPrediction();
            BigDecimal sy = null;
            BigDecimal maxPrice = prediction.getMaxPrice();
            BigDecimal minPrice = prediction.getMinPrice();
            stockIncomePrediction.setPredictionMaxPrice(maxPrice);
            stockIncomePrediction.setPredictionMinPrice(minPrice);
            stockIncomePrediction.setBuyPrice(buyPrice);
            boolean upFlag = data.getOpenPrice().compareTo(data.getClosePrice()) <= 0;

            String info = upFlag ? "上涨" : "下跌";
            BigDecimal rangePrice = data.getOpenPrice().subtract(data.getClosePrice()).abs();
            log.info("{} 真实股价信息:最高价={},最低价={},开盘价={},收盘价={},涨跌:{}", data.getDate(), data.getMaxPrice(),
                data.getMinPrice(), data.getOpenPrice(), data.getClosePrice(), info + rangePrice);

            if (checkInfo.isMatchAll()) {
                sy = maxPrice.subtract(buyPrice);
                log.info("{} 全匹配,预测出售价:{},预测买入价:{},上次买入价:{},真实出售价:{},真实买入价:{},收益:{},截止昨日总收益:{}", data.getDate(),
                    maxPrice, minPrice, buyPrice, maxPrice, minPrice, sy, total);
                if (CollectionUtils.isNotEmpty(notSellBuyList)) {
                    BigDecimal temp = sellStoreStock(data.getDate(), notSellBuyList, maxPrice);
                    sy = sy.add(temp);
                    notSellBuyList = new ArrayList<>();
                }
                buyPrice = minPrice;
            } else if (checkInfo.isMatchSell()) {
                //买入价以下午2.30的基准价买入,暂处理以收盘价买入
                sy = maxPrice.subtract(buyPrice);
                BigDecimal realBuyPrice = getCompensateBuyPrice(data, historyMinuteData);
                log.info("{} 匹配出售价,预测出售价:{},预测买入价:{},上次买入价:{},真实出售价:{},真实买入价:{},收益:{},截止昨日总收益:{}", data.getDate(),
                    maxPrice, minPrice, buyPrice, maxPrice, realBuyPrice, sy, total);
                if (CollectionUtils.isNotEmpty(notSellBuyList)) {
                    BigDecimal temp = sellStoreStock(data.getDate(), notSellBuyList, maxPrice);
                    sy = sy.add(temp);
                    notSellBuyList = new ArrayList<>();
                }
                buyPrice = realBuyPrice;
            } else if (checkInfo.isMatchBuy()) {

                //卖出价以上午10.30的基准价卖出,暂处理以收盘价卖出
                sy = data.getClosePrice().subtract(buyPrice);
                BigDecimal realSellPrice = getCompensateSellPrice(data, historyMinuteData);
                log.info("{} 匹配买入价,预测出售价:{},预测买入价:{},上次买入价:{},真实出售价:{},真实买入价:{},收益:{},截止昨日总收益:{}", data.getDate(),
                    maxPrice, minPrice, buyPrice, realSellPrice, minPrice, sy, total);

                //特殊处理,当未匹配最高价时，且明天预测最低价比收盘价都要高,那就不卖了,等到明天再说
                //if (tomorroPrediction.getMinPrice().compareTo(data.getClosePrice()) >= 0) {
                //    notSellBuyList.add(buyPrice);
                //    log.info("{} 特殊卖出逻辑,暂时不卖了,等明天再说,不卖的成本价:{}", data.getDate(), notSellBuyList, buyPrice);
                //}else{
                //    if (CollectionUtils.isNotEmpty(notSellBuyList)) {
                //        BigDecimal temp = sellStoreStock(data.getDate(), notSellBuyList, maxPrice);
                //        sy = sy.add(temp);
                //        notSellBuyList = new ArrayList<>();
                //    }
                //}
                buyPrice = minPrice;
            } else if (!upFlag && rangePrice.compareTo(new BigDecimal(5000)) >= 0) {
                //大跌超过5分钱,以收盘价卖出,割肉离场,休息3天
                //实测好像没啥用,把价格改成5000，让这段逻辑失效
                sy = data.getClosePrice().subtract(buyPrice);
                log.info("{} 大跌超过4分,割肉离场,预测出售价:{},预测买入价:{},上次买入价:{},真实出售价:{},真实买入价:{},收益:{},截止昨日总收益:{}", data.getDate(),
                    maxPrice, minPrice, buyPrice, data.getClosePrice(), minPrice, sy, total);
                buyPrice = null;
                sleepCount = 4;
            } else {
                //以收盘价卖出，收盘价买入
                sy = data.getClosePrice().subtract(buyPrice);
                log.info("{} 未匹配,预测出售价:{},预测买入价:{},上次买入价:{},真实出售价:{},真实买入价:{},收益:{},截止昨日总收益:{}", data.getDate(),
                    maxPrice, minPrice, buyPrice, data.getClosePrice(), data.getClosePrice(), sy, total);
                ////特殊处理,当未匹配最高价时，且明天预测最低价比收盘价都要高,那就不卖了,等到明天再说
                //if (tomorroPrediction.getMinPrice().compareTo(data.getClosePrice()) >= 0) {
                //    notSellBuyList.add(buyPrice);
                //    log.info("{} 特殊卖出逻辑,暂时不卖了,等明天再说,不卖的成本价:{}", data.getDate(), notSellBuyList,buyPrice);
                //}
                if (CollectionUtils.isNotEmpty(notSellBuyList)) {
                    BigDecimal temp = sellStoreStock(data.getDate(), notSellBuyList, maxPrice);
                    sy = sy.add(temp);
                    notSellBuyList = new ArrayList<>();
                }
                buyPrice = data.getClosePrice();
            }
            stockIncomePrediction.setIncome(sy);
            if (!first) {
                total = total.add(sy);
            } else {
                first = false;
            }
        }
        log.info("总收益为{}", total);
        return stockIncomePredictions;
    }

    /**
     * 获取股票的预测数据
     *
     * @param code
     * @param year
     * @return
     */
    public static List<StockDayData> predictionStock(String code, String year) {
        List<StockDayData> stockDayDataList = StockUtils.getHistoryDataByYear(code, year);
        return predictionStock(stockDayDataList);
    }

    /**
     * 获取股票的预测数据
     *
     * @param stockDayDataList
     * @return
     */
    public static List<StockDayData> predictionStock(List<StockDayData> stockDayDataList) {
        if (CollectionUtils.isEmpty(stockDayDataList)) {
            return null;
        }
        int matchSellCount = 0;
        int matchBuyCount = 0;
        int matchAllCount = 0;
        int eqSellCount = 0;
        int eqBuyCount = 0;
        int eqAllCount = 0;
        StockPredictionData predictionData = new StockPredictionData();
        predictionData.setMaxPrice(stockDayDataList.get(0).getMaxPrice());
        predictionData.setMinPrice(stockDayDataList.get(0).getMinPrice());
        StockPredictionCheckInfo checkInfo = null;
        List<String> correctDays = Lists.newArrayList();
        String code = stockDayDataList.get(0).getCode();
        for (StockDayData stockDayData : stockDayDataList) {
            stockDayData.setTodayPrediction(BeanConvertUtils.convert(predictionData, StockPredictionData.class));
            checkInfo = checkPrediction(predictionData, stockDayData);
            stockDayData.setCheckInfo(checkInfo);
            if (checkInfo.isMatchSell()) {
                matchSellCount++;
            }
            if (checkInfo.isMatchBuy()) {
                matchBuyCount++;
            }
            if (checkInfo.isMatchAll()) {
                matchAllCount++;
            }
            if (checkInfo.isEqSell()) {
                eqSellCount++;
            }
            if (checkInfo.isEqBuy()) {
                eqBuyCount++;
            }
            if (checkInfo.isEqAll()) {
                eqAllCount++;
            }
            predictionData = predictionStock(stockDayData, correctDays);
            stockDayData.setTomorrowPrediction(predictionData);
            log.info("{}:{} 真实股价信息:最高价={},最低价={},开盘价={},收盘价={},今日命中={},预测明日:最高价={},最低价={},",
                code, stockDayData.getDate(),
                stockDayData.getMaxPrice(), stockDayData.getMinPrice(), stockDayData.getOpenPrice(),
                stockDayData.getClosePrice(), JSON.toJSONString(checkInfo),
                predictionData.getMaxPrice(), predictionData.getMinPrice()
                );
        }
        log.info("{} 总次数:{},命中最高价次数:{},命中最低价次数:{},全命中次数:{},等于卖出价次数:{},等于买入价次数:{},全等于次数:{}",
            code, stockDayDataList.size(),
            matchSellCount, matchBuyCount, matchAllCount, eqSellCount, eqBuyCount, eqAllCount);
        StockDayData lastData = stockDayDataList.get(stockDayDataList.size() - 1);
        lastData.setTomorrowPrediction(predictionData);
        lastData.setCheckInfo(checkInfo);
        return stockDayDataList;

    }

    public static BigDecimal caculateTotalIncome(List<StockIncomePrediction> stockIncomePredictions) {
        BigDecimal total = new BigDecimal(0);
        for (StockIncomePrediction prediction : stockIncomePredictions) {
            total.add(prediction.getIncome());
        }
        return total;
    }

    /**
     * 未成交时补偿卖出价
     *
     * @param stockDayData
     * @param historyMinuteData
     * @return
     */
    private static BigDecimal getCompensateSellPrice(StockDayData stockDayData,
        List<StockMinuteData> historyMinuteData) {
        //if (CollectionUtils.isEmpty(historyMinuteData)) {
        //    return stockDayData.getClosePrice();
        //}
        //
        //String date = stockDayData.getDate();
        //for (StockMinuteData minuteData : historyMinuteData) {
        //    if (minuteData.getDate().equals(date) && minuteData.getMinute().equals("14:30:00")) {
        //        //14:30的价格比开盘价低，说明趋势下跌，用14:30的价格去卖
        //        if(stockDayData.getOpenPrice().compareTo(minuteData.getPrice())>0) {
        //            return minuteData.getPrice();
        //        }
        //    }
        //}
        return stockDayData.getClosePrice();
    }

    /**
     * 未成交时补偿买入价
     *
     * @param stockDayData
     * @param historyMinuteData
     * @return
     */
    private static BigDecimal getCompensateBuyPrice(StockDayData stockDayData,
        List<StockMinuteData> historyMinuteData) {
        //if (CollectionUtils.isEmpty(historyMinuteData)) {
        //    return stockDayData.getClosePrice();
        //}
        //boolean upFlag = stockDayData.getOpenPrice().compareTo(stockDayData.getClosePrice()) <= 0;
        //if (!upFlag) {
        //    return stockDayData.getClosePrice();
        //}
        //String date = stockDayData.getDate();
        //for (StockMinuteData minuteData : historyMinuteData) {
        //    if (minuteData.getDate().equals(date)
        //        && (minuteData.getMinute().equals("14:30:00"))) {
        //        //14:30的价格比开盘价高，说明趋势上涨，用14:30的价格去买,否则用尾盘价买
        //        if(stockDayData.getOpenPrice().compareTo(minuteData.getPrice())<=0) {
        //            return minuteData.getPrice();
        //        }
        //    }
        //}
        return stockDayData.getClosePrice();
    }

    /**
     * 出售库存股票
     *
     * @param buyPriceList
     * @param sellPrice
     */
    private static BigDecimal sellStoreStock(String date, List<BigDecimal> buyPriceList, BigDecimal sellPrice) {
        BigDecimal sy = new BigDecimal(0);
        for (BigDecimal buyPrice : buyPriceList) {
            sy = sy.add(sellPrice).subtract(buyPrice);
        }
        log.info("{} 出售库存股票,真实出售价:{},真实买入价:{},收益:{}", date, sellPrice,
            buyPriceList, sy);
        return sy;
    }

    //public static void main2(String[] args) {
    //    List<String> codes = Lists.newArrayList(
    //        "sh601518",
    //        "sh600170",
    //        "sh600467",
    //        "sh600162",
    //        "sh600321",
    //        "sh600221",
    //        "sh600022",
    //        "sz002501",
    //        "sz300116",
    //        "sz000982",
    //        "sh600307",
    //        "sh600871",
    //        "sh600569",
    //        "sh600010",
    //        "sh600157",
    //        "sh600726",
    //        "sz000861",
    //        "sh600527",
    //        "sh601880",
    //        "sh601005"
    //    );
    //    Map<String, List<Pair<String, BigDecimal>>> map = new HashMap<>();
    //    int startYear = 2016;
    //    int yearCount = 2023 - startYear;
    //    for (String code : codes) {
    //        List<Pair<String, BigDecimal>> totals = new ArrayList<>();
    //        for (int i = 0; i <= yearCount; i++) {
    //            String year = (startYear + i) + "";
    //            List<StockDayData> stockDayDataList = predictionStock(code, year);
    //            BigDecimal averagePrice = StockUtils.getAveragePrice(stockDayDataList);
    //            BigDecimal total = null;
    //            //
    //            if (averagePrice.compareTo(new BigDecimal(3)) >= 0) {
    //                total = averagePrice.add(new BigDecimal(1000000));
    //            } else {
    //                total = caculatePrice(stockDayDataList);
    //            }
    //            if (Objects.isNull(total)) {
    //                break;
    //            }
    //            Pair<String, BigDecimal> pair = new Pair<>(year, total);
    //            totals.add(pair);
    //        }
    //        map.put(code, totals);
    //    }
    //    System.out.println(JSON.toJSONString(map));
    //    for (Entry<String, List<Pair<String, BigDecimal>>> entry : map.entrySet()) {
    //        List<Pair<String, BigDecimal>> list = entry.getValue();
    //        BigDecimal sum = new BigDecimal(0);
    //        System.out.print(entry.getKey() + ",");
    //        for (Pair<String, BigDecimal> pair : list) {
    //            sum = sum.add(pair.getValue());
    //            System.out.print(pair.getValue() + ",");
    //        }
    //        System.out.println();
    //        //System.out.println(entry.getKey() + "总收益:" + sum);
    //    }
    //
    //}
    //
    //public static void main(String[] args) {
    //    validateYesterday("2023");
    //    //guessTomorrow("2023");
    //}
    //
    //public static void validateYesterday(String year) {
    //    List<String> codes = Lists.newArrayList(
    //        "sh601005",
    //        "sh600569"
    //        //"sh600157",
    //        //"sz000861",
    //        //"sh600307",
    //        //"sh600527"
    //    );
    //    List<Pair<String, BigDecimal>> result = new ArrayList<>();
    //    List<StockPredictionData> tomorrowPredictions = new ArrayList<>();
    //    List<StockDayData> todayDatas = new ArrayList<>();
    //
    //    for (String code : codes) {
    //        List<StockDayData> stockDayDataList = StockUtils.getHistoryDataByYear(code, year);
    //        stockDayDataList.add(getTodayStockData(code));
    //        stockDayDataList = predictionStock(stockDayDataList);
    //        tomorrowPredictions.add(stockDayDataList.get(stockDayDataList.size() - 1).getTomorrowPrediction());
    //        todayDatas.add(stockDayDataList.get(stockDayDataList.size() - 1));
    //        log.info("#################以下计算收益##########################");
    //        BigDecimal total = caculatePrice(stockDayDataList);
    //        Pair<String, BigDecimal> pair = new Pair<>(code, total);
    //        result.add(pair);
    //        System.out.println("==================================================");
    //    }
    //
    //    log.info("#################以下为今日验证结果,工作日下午2.50分以后有效##########################");
    //    for (StockDayData data : todayDatas) {
    //        log.info("{} {} 真实股价信息:最高价={},最低价={},开盘价={},收盘价={},今日预测信息:最高价={},最低价={},卖价匹配:{},买价匹配:{}", data.getDate(),
    //            data.getCode(), data.getMaxPrice(), data.getMinPrice(), data.getOpenPrice(), data.getClosePrice(),
    //            data.getTodayPrediction().getMaxPrice(), data.getTodayPrediction().getMinPrice(),
    //            data.getCheckInfo().isMatchSell(), data.getCheckInfo().isMatchBuy());
    //    }
    //    log.info("总收益计算,year:{},result:{}", year, JSON.toJSONString(result));
    //}
    //
    //public static void guessTomorrow(String year) {
    //    List<String> codes = Lists.newArrayList(
    //        "sh601005",
    //        "sh600569",
    //        "sh600157",
    //        "sz000861",
    //        "sh600307",
    //        "sh600527"
    //    );
    //    List<Pair<String, BigDecimal>> result = new ArrayList<>();
    //    List<StockPredictionData> tomorrowPredictions = new ArrayList<>();
    //    List<StockDayData> todayDatas = new ArrayList<>();
    //
    //    for (String code : codes) {
    //        List<StockDayData> stockDayDataList = StockUtils.getHistoryDataByYear(code, year);
    //        //加入今天数据，预测明天的，如果是要预测今天的，就不需要加
    //        stockDayDataList.add(getTodayStockData(code));
    //        stockDayDataList = predictionStock(stockDayDataList);
    //        tomorrowPredictions.add(stockDayDataList.get(stockDayDataList.size() - 1).getTomorrowPrediction());
    //        todayDatas.add(stockDayDataList.get(stockDayDataList.size() - 2));
    //        log.info("#################以下计算收益##########################");
    //        BigDecimal total = caculatePrice(stockDayDataList);
    //        Pair<String, BigDecimal> pair = new Pair<>(code, total);
    //        result.add(pair);
    //        System.out.println("==================================================");
    //    }
    //    log.info("#################以下为预测结果,工作日下午2.50分以后有效##########################");
    //    log.info(JSON.toJSONString(tomorrowPredictions));
    //
    //}
}
